#include "MyXmppClient.h"

#include "QXmppLogger.h"
#include "QXmppRosterManager.h"
#include "QXmppVersionManager.h"
#include "QXmppConfiguration.h"

#include <QDebug>
#include <QCryptographicHash>


QString MyXmppClient::myJid = "";
QString MyXmppClient::myPass = "";
QString MyXmppClient::myVersion = "0.0.1";

MyXmppClient::MyXmppClient() : QDeclarativeView(0)
{
    QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging);

    cacheIM = new MyCache(this);
    msgWrapper = new MessageWrapper(this);

    xmppClient = new QXmppClient( this );
    QObject::connect( xmppClient, SIGNAL(connected()), this, SLOT(clientConnected()) );
    QObject::connect( xmppClient, SIGNAL(stateChanged(QXmppClient::State)), this, SLOT(clientStateChanged(QXmppClient::State)) );
    QObject::connect( xmppClient, SIGNAL(messageReceived(QXmppMessage)), msgWrapper, SLOT(messageReceived(QXmppMessage)) );

    QObject::connect( msgWrapper, SIGNAL(openChat(QString)), this, SLOT(openChat(QString)) );
    QObject::connect( msgWrapper, SIGNAL(sendMyMsg(QXmppPacket)), xmppClient, SLOT(sendPacket(QXmppPacket)) );

    xmppMessageReceiptManager = new QXmppMessageReceiptManager();
    QObject::connect( xmppMessageReceiptManager, SIGNAL(messageDelivered(QString,QString)), msgWrapper, SLOT(messageDelivered(QString,QString)) );

    QDeclarativeEngine *engine = this->engine();
    engine->rootContext()->setContextProperty( "cppMyXmppClient", this );
    engine->rootContext()->setContextProperty("cppMessages", msgWrapper);

    listModelChats = new ChatsListModel( this );
    engine->rootContext()->setContextProperty( "cppListOfChats", listModelChats );

    mimOpt = new MeegIMSettings( this );
    engine->rootContext()->setContextProperty( "cppMeegIMSettings", mimOpt );

    flShowOffline = false;

    cppMeegIMStatus = "offline";

    listModelRoster = new RosterListModel( this );
    engine->rootContext()->setContextProperty( "cppRoster", listModelRoster );

}


MyXmppClient::~MyXmppClient()
{
    /*if( xmppClient != NULL ) {
        delete xmppClient;
    }*/

    if( cacheIM != NULL) {
        delete cacheIM;
    }

    if( msgWrapper != NULL) {
        delete msgWrapper;
    }

    /*if( xmppMessageReceiptManager != NULL) {
        delete xmppMessageReceiptManager;
    }*/
}



void MyXmppClient::initXmppClient()
{
    /* init home directory */
    /*bool res = */cacheIM->createHomeDir();

    xmppClient->versionManager().setClientName("MeegIM");
    xmppClient->versionManager().setClientVersion( MyXmppClient::myVersion );

    mimOpt->getDefaultAccount();

    this->setSource( QUrl("qrc:/qml/main.qml") );

    /* add extension */
    xmppClient->addExtension( xmppMessageReceiptManager );

    /* получение объекта только после this->setSource() */
    qmlXmppClient =QMLXmppClient::getInstance();
    qDebug() << "initXmppClient: qmlXmppClient:[" << qmlXmppClient << "]";

    //this->connectToXmppServer();
}



void MyXmppClient::clientConnected()
{
    qDebug() << "MyXmppClient::clientConnected() ok";
}



void MyXmppClient::clientStateChanged(QXmppClient::State state)
{
    QObject *pageRoster = this->rootObject()->findChild<QObject*>( QString("rosterPage") );

    if( state == QXmppClient::ConnectingState ) {
        qDebug() << "MyXmppClient::clientStateChanged(): ConnectingState";
        QMetaObject::invokeMethod( pageRoster, "statConnection");
    } else if( state == QXmppClient::ConnectedState ) {
        qDebug() << "MyXmppClient::clientStateChanged(): ConnectedState";

        QMetaObject::invokeMethod( pageRoster, "statConnected");

        rosterManager = &xmppClient->rosterManager();
        QObject::connect( rosterManager, SIGNAL(rosterReceived()), this, SLOT(initRoster()) );
        QObject::connect( rosterManager, SIGNAL(presenceChanged(QString,QString)), this, SLOT(initPresence(const QString, const QString)) );

    } else if( state == QXmppClient::DisconnectedState ) {
        QMetaObject::invokeMethod( pageRoster, "statDisonnected");
        qDebug() << "MyXmppClient::clientStateChanged(): DisconnectedState";
    }
}



void MyXmppClient::connectToXmppServer()
{
    QXmppConfiguration xmppConfig;

    xmppConfig.setHost("xmpp.yandex.ru");
    //xmppConfig.setDomain("ya.ru");
    xmppConfig.setPassword( MyXmppClient::myPass );
    xmppConfig.setJid(MyXmppClient::myJid);
    xmppConfig.setResource( "MeegIM" );
    xmppClient->connectToServer( xmppConfig );
    //xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
    //TODO: здесь восстанавливть последний статус
}



/* it initialises the list of contacts - roster */
void MyXmppClient::initRoster()
{
    vCardManager = &xmppClient->vCardManager();
    QObject::connect( vCardManager, SIGNAL(vCardReceived(const QXmppVCardIq &)),
                      this, SLOT(initVCard(const QXmppVCardIq &)),
                      Qt::UniqueConnection  );

    QStringList listBareJids = rosterManager->getRosterBareJids();
    for( int j=0; j < listBareJids.length(); j++ )
    {
        QString bareJid = listBareJids.at(j);

        cacheIM->addCacheJid( bareJid );

        QXmppRosterIq::Item itemRoster = rosterManager->getRosterEntry( bareJid );
        QString name = itemRoster.name();
        QList<QString> listOfGroup = itemRoster.groups().toList();
        QString group = "";
        if( listOfGroup.length() > 0 ) {
            group = listOfGroup.at(0);
        }

        /* если в кеше есть аватарка и vCard, получим их */
        QString avatarPath = cacheIM->getAvatarCache( bareJid );
        vCardData vCdata = cacheIM->getVCard( bareJid );

        if( avatarPath.isEmpty() && vCdata.isEmpty() ) {
            /* кеш пустой, запрос vCard для текущего bareJid */
            vCardManager->requestVCard( bareJid );
            //qDebug() << "* request vCard for " << bareJid;
        } else {
            if( !avatarPath.isEmpty() ) {
                avatarPath = QString("file://") + avatarPath;
            }
            QString nickName = vCdata.nickName;
            if( (!nickName.isEmpty()) && (!nickName.isNull()) && (itemRoster.name().isEmpty()) ) {
                name =  nickName;
            }
            //qDebug() << "* set from a cache: " << bareJid;
        }

        RosterItemModel *itemModel = new RosterItemModel( );
        itemModel->setGroup( group );
        itemModel->setPicStatus( this->getPicPresence( QXmppPresence::Status::Offline ) );
        itemModel->setContactName( name );
        itemModel->setJid( bareJid );
        itemModel->setAvatar( avatarPath );

        listRoster[ bareJid ] = itemModel;
    }
    //qDebug() << listRoster;

    setQMLListRoster();
}



/* it get */
void MyXmppClient::initPresence(const QString& bareJid, const QString& resource)
{
    if( !listRoster.contains( bareJid ) ) {
        return;
    }

    RosterItemModel *item = listRoster[ bareJid ];

    item->setResource( resource );

    QXmppPresence xmppPresence = rosterManager->getPresence( bareJid, resource );
    QXmppPresence::Status::Type statusJid = xmppPresence.status().type();
    QString picStatus = this->getPicPresence( statusJid );
    item->setPicStatus( picStatus );

    QString txtStatus = this->getTextStatus( xmppPresence.status().statusText(), statusJid );
    item->setTextStatus( txtStatus );
    qDebug() << bareJid << " => " << resource << " => " << txtStatus ;

    /* ищем контакт в ростере */
    int indxItem = -1;
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid, indxItem );

    if( flShowOffline )
    {
        /* показать offline контакты */
        if( itemExists != 0 ) {
            /* обновим информацию */
            itemExists->copy( item );
        }
    }
    else
    {
        /* скрыть оффлайн контакты */
        if( (itemExists==0) && (statusJid != QXmppPresence::Status::Offline ) ) {
            /* добавляем контакт в ростер */
            listModelRoster->append( item );
        } else if( (itemExists!=0) && (statusJid != QXmppPresence::Status::Offline ) ) {
            /* обновить данные на ростере */
            itemExists->copy( item );
        } else if( (itemExists!=0) && (statusJid == QXmppPresence::Status::Offline ) ) {
            /* удаляем контакт из ростера */
            if( indxItem >= 0 ) {
                listModelRoster->takeRow( indxItem );
            }
        }
    }

}


void MyXmppClient::setQMLListRoster( )
{
    listModelRoster->takeRows(0, listModelRoster->count() );
    if( flShowOffline )
    {
        QMap< QString, RosterItemModel*>::iterator itr;
        for ( itr = listRoster.begin(); itr != listRoster.end(); itr++ )
        {
            //QString jid = itr.key();
            RosterItemModel* itemData = itr.value();
            listModelRoster->append( itemData );
        }
    }
}


QString MyXmppClient::getPicPresence(const QXmppPresence::Status::Type &status) const
{
    QString picPresenceName = "qrc:/qml/images/presence-unknown.png";

    if( (status == QXmppPresence::Status::Online) || (status == QXmppPresence::Status::Chat) ) {
        picPresenceName = "qrc:/qml/images/presence-online.png";
    } else if ( (status == QXmppPresence::Status::Away) || (status == QXmppPresence::Status::XA) ) {
        picPresenceName = "qrc:/qml/images/presence-away.png";
    } else if ( status == QXmppPresence::Status::DND ) {
        picPresenceName = "qrc:/qml/images/presence-busy.png";
    } else if ( status == QXmppPresence::Status::Offline ) {
        picPresenceName = "qrc:/qml/images/presence-offline.png";
    }

    return picPresenceName;
}



QString MyXmppClient::getTextStatus(const QString &textStatus, const QXmppPresence::Status::Type &status ) const
{
    if( (!textStatus.isEmpty()) && (!textStatus.isNull()) ) {
        return textStatus;
    }

    QString txtStat = "";
    if( status == QXmppPresence::Status::Online ) {
        txtStat = "Online";
    } else if ( status == QXmppPresence::Status::Chat ) {
        txtStat = "Chat";
    } else if ( status == QXmppPresence::Status::Away ) {
        txtStat = "Away";
    } else if ( status == QXmppPresence::Status::XA ) {
        txtStat = "XA";
    } else if ( status == QXmppPresence::Status::DND ) {
        txtStat = "DND";
    } else if ( status == QXmppPresence::Status::Offline ) {
        txtStat = "Offline";
    }

    return txtStat;
}



/* SLOT: it will be called when the vCardReceived signal will be received */
void MyXmppClient::initVCard(const QXmppVCardIq &vCard)
{
    QString bareJid = vCard.from();
    //qDebug() << "## initVCard: " << bareJid;

    RosterItemModel *item = (RosterItemModel*)listModelRoster->find( bareJid );

    vCardData dataVCard;

    if( item != 0 )
    {
        /* set nickname */
        QXmppRosterIq::Item itemRoster = rosterManager->getRosterEntry( bareJid );
        QString nickName = vCard.nickName();
        if( (!nickName.isEmpty()) && (!nickName.isNull()) && (itemRoster.name().isEmpty()) ) {
            item->setContactName( nickName );
        }

        /* avatar */
        bool isAvatarCreated = true;
        QString avatarFile = cacheIM->getAvatarCache( bareJid );
        if( avatarFile.isEmpty() ) {
            isAvatarCreated =  cacheIM->setAvatarCache( bareJid, vCard.photo() );
            avatarFile = cacheIM->getAvatarCache( bareJid );
        }
        if( isAvatarCreated ) {
            QString f = QString("file://") + avatarFile;
            item->setAvatar( f );
        }

        dataVCard.nickName = nickName;
        dataVCard.firstName = vCard.firstName();
        dataVCard.fullName = vCard.fullName();;
        dataVCard.middleName = vCard.middleName();
        dataVCard.lastName = vCard.lastName();
        dataVCard.url = vCard.url();
        dataVCard.eMail = vCard.email();

        //qDebug() << dataVCard.nickName;
        //qDebug() << dataVCard.firstName;
        //qDebug() << dataVCard.fullName;
        //qDebug() << dataVCard.middleName;
        //qDebug() << dataVCard.lastName;
        //qDebug() << dataVCard.url;
        //qDebug() << dataVCard.eMail;

        cacheIM->setVCard( bareJid, dataVCard );
        //qDebug() << "----------------------------";
    }

}



void MyXmppClient::setMyPresence( QString status, QString textStatus ) //Q_INVOKABLE
{
    qDebug() << "setMyPresence():" << status << " " << textStatus;
    if( status == "online" )
    {
        cppMeegIMStatus = "online";
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
        }
        QXmppPresence myPresence(QXmppPresence::Available, QXmppPresence::Status::Online);
        myPresence.status().setStatusText( textStatus );
        xmppClient->setClientPresence( myPresence  );
    }
    else if( status == "chat" )
    {
        cppMeegIMStatus = "chat";
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
        }
        QXmppPresence myPresence(QXmppPresence::Available, QXmppPresence::Status::Chat);
        myPresence.status().setStatusText( textStatus );
        xmppClient->setClientPresence( myPresence  );
    }
    else if( status == "away" )
    {
        cppMeegIMStatus = "away";
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
        }
        QXmppPresence myPresence(QXmppPresence::Available, QXmppPresence::Status::Away);
        myPresence.status().setStatusText( textStatus );
        xmppClient->setClientPresence( myPresence  );
    }
    else if( status == "XA" )
    {
        cppMeegIMStatus = "XA";
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
        }
        QXmppPresence myPresence(QXmppPresence::Available, QXmppPresence::Status::XA);
        myPresence.status().setStatusText( textStatus );
        xmppClient->setClientPresence( myPresence  );
    }
    else if( status == "DND" )
    {
        cppMeegIMStatus = "DND";
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            xmppClient->connectToServer( MyXmppClient::myJid, MyXmppClient::myPass );
        }
        QXmppPresence myPresence(QXmppPresence::Available, QXmppPresence::Status::DND);
        myPresence.status().setStatusText( textStatus );
        xmppClient->setClientPresence( myPresence  );
    }
    else if( status == "offline" )
    {
        cppMeegIMStatus = "offline";
        if( (xmppClient->state()  == QXmppClient::ConnectedState)  || (xmppClient->state()  == QXmppClient::ConnectingState) )
        {
            xmppClient->disconnectFromServer();
        }
        QXmppPresence myPresence(QXmppPresence::Unavailable, QXmppPresence::Status::Offline);
        xmppClient->setClientPresence( myPresence  );

        /* все контакты offline */
        QMap< QString, RosterItemModel*>::iterator itr;
        qDebug() << "zz1:"<<listRoster.count();
        for ( itr = listRoster.begin(); itr != listRoster.end(); itr++ )
        {
            RosterItemModel* itemData = itr.value();
            QString pic = this->getPicPresence( QXmppPresence::Status::Offline );
            itemData->setPicStatus( pic );
            QString st = this->getTextStatus( "", QXmppPresence::Status::Offline );
            itemData->setTextStatus( st );
        }
        this->setQMLListRoster();
    }
}


/* it sends information about typing : typing is started */
void MyXmppClient::typingStart(QString bareJid) //Q_INVOKABLE
{
    //qDebug() << bareJid << " " << "start typing...";
    QXmppMessage xmppMsg;
    xmppMsg.setTo( bareJid );
    xmppMsg.setFrom( MyXmppClient::myJid );
    xmppMsg.setReceiptRequested( false );

    QDateTime currTime = QDateTime::currentDateTime();
    xmppMsg.setStamp( currTime );

    xmppMsg.setState( QXmppMessage::Composing );

    xmppClient->sendPacket( xmppMsg );
}


/* it sends information about typing : typing is stoped */
void MyXmppClient::typingStop(QString bareJid) //Q_INVOKABLE
{
    //qDebug() << bareJid << " " << "stop typing...";
    QXmppMessage xmppMsg;
    xmppMsg.setTo( bareJid );
    xmppMsg.setFrom( MyXmppClient::myJid );
    xmppMsg.setReceiptRequested( false );

    QDateTime currTime = QDateTime::currentDateTime();
    xmppMsg.setStamp( currTime );

    xmppMsg.setState( QXmppMessage::Paused );

    xmppClient->sendPacket( xmppMsg );
}



void MyXmppClient::openChat( QString bareJid ) //Q_INVOKABLE
{
    qDebug() << "new chat was opened:" << bareJid;
    RosterItemModel *itemRoster =  reinterpret_cast<RosterItemModel*>( listModelRoster->find( bareJid ) );
    if( itemRoster == NULL ) {
        return;
    }

    RosterItemModel* item = reinterpret_cast<RosterItemModel*>( listModelChats->find( bareJid ) );
    if( item == NULL ) { // исключаем вторичное добавление
        listModelChats->append(itemRoster);
    }
}


void MyXmppClient::closeChat( QString bareJid ) //Q_INVOKABLE
{
    int row = -1;
    RosterItemModel *item = reinterpret_cast<RosterItemModel*>( listModelChats->find( bareJid, row ) );
    if( (item != NULL) && (row >= 0) ) {
        bool res = listModelChats->takeRow( row );
        qDebug() << "MyXmppClient::closeChat("<<bareJid<<"): row:"<<row << " result:"<<res << " listModelChats.count():" <<listModelChats->count();
    }
    msgWrapper->removeListOfChat( bareJid );
}



